/*
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2006-2007 Intel Corporation. All Rights Reserved.
//
*/
#include "umc_defs.h"
#if defined (UMC_ENABLE_DV_VIDEO_ENCODER)

#include "umc_dv_enc_dvsd_ntsc_segment_reader.h"
#include "umc_dv_enc_segment_compressor.h"
#include "umc_dv_enc_block.h"

namespace UMC
{

DVSD_NTSC_SegmentReader::DVSD_NTSC_SegmentReader(Ipp16s nNumOfDIFSeq):
    SegmentReader(nNumOfDIFSeq)
{
}

DVSD_NTSC_SegmentReader::~DVSD_NTSC_SegmentReader(void)
{
}

static
inline
void ReadYLine(Ipp16s *lpBlockYData, Ipp16s LineNum, Ipp8u *lpcYSrc)
{
    Ipp32s iX;
    for(iX=0; iX<8; iX++)
        lpBlockYData[iX + LineNum*8] = ((Ipp16s) lpcYSrc[iX]) - 128;
}

static
inline
void ReadUVLine(Ipp16s *lpBlockUVData, Ipp16s LineNum, Ipp8u *lpcUVSrc)
{
    Ipp32s iX;
    for(iX=0; iX<8; iX++)
    {
        lpBlockUVData[iX +  LineNum*8]    =
        lpBlockUVData[iX + (LineNum+1)*8] =
            ((Ipp16s) (lpcUVSrc[iX*2]+lpcUVSrc[iX*2+1])/2) - 128;
    }
}

static
inline
void ReadHalfUVLine(Ipp16s *lpBlockUVData, Ipp16s LineNum, Ipp8u *lpcUVSrc)
{
    Ipp32s iX;
    for(iX=0; iX<4; iX++)
    {
        lpBlockUVData[iX +  LineNum*8]    =
        lpBlockUVData[iX + (LineNum+1)*8] =
        ((Ipp16s) (lpcUVSrc[iX*2]+lpcUVSrc[iX*2+1])/2) - 128;
    }
}

static
void ReadYUV(size_t *YOffsets, size_t *UOffsets, size_t *VOffsets,
             V_SEGMENT *lpVSegment, VideoData *pSrcFrame)
{
    Ipp8u *lpcSrcY, *lpcSrcU, *lpcSrcV;
    Ipp16s LineNum, MBNum;
    BLOCK *Y0, *Y1, *Y2, *Y3, *Cr, *Cb;

    for (MBNum = 0; MBNum < 5; MBNum++)
    {
        lpcSrcY = (Ipp8u*)pSrcFrame->GetPlanePointer(0) + YOffsets[MBNum];

        Y0 = lpVSegment->m_pDCTBlocks + MBNum*6;
        Y1 = Y0 + 1;
        Y2 = Y0 + 2;
        Y3 = Y0 + 3;

        for (LineNum = 0; LineNum < 8; LineNum++)
        {
            ReadYLine(Y0->m_lpsData, LineNum, lpcSrcY + 8*0);
            ReadYLine(Y1->m_lpsData, LineNum, lpcSrcY + 8*1);
            ReadYLine(Y2->m_lpsData, LineNum, lpcSrcY + 8*2);
            ReadYLine(Y3->m_lpsData, LineNum, lpcSrcY + 8*3);

            lpcSrcY += pSrcFrame->GetPlanePitch(0);
        }
    }

    for (MBNum = 0; MBNum < 5; MBNum++)
    {
        lpcSrcU = (Ipp8u*)pSrcFrame->GetPlanePointer(1) + UOffsets[MBNum];
        lpcSrcV = (Ipp8u*)pSrcFrame->GetPlanePointer(2) + VOffsets[MBNum];

        Cr = lpVSegment->m_pDCTBlocks + MBNum*6 + 4;
        Cb = Cr + 1;

        for (LineNum = 0;LineNum < 4;LineNum++)
        {
            ReadUVLine(Cr->m_lpsData, LineNum*2, lpcSrcV);
            ReadUVLine(Cb->m_lpsData, LineNum*2, lpcSrcU);

            lpcSrcU += pSrcFrame->GetPlanePitch(1);
            lpcSrcV += pSrcFrame->GetPlanePitch(2);
        }
    }
}

static
void ReadEndYUV(size_t *YOffsets, size_t *UOffsets, size_t *VOffsets,
                V_SEGMENT *lpVSegment, VideoData *pSrcFrame)
{
    Ipp8u *lpcSrcY, *lpcSrcU, *lpcSrcV;
    Ipp16s LineNum, MBNum;
    BLOCK *Y0, *Y1, *Y2, *Y3, *Cr, *Cb;

    for (MBNum = 0; MBNum < 4; MBNum++)
    {
        lpcSrcY = (Ipp8u*)pSrcFrame->GetPlanePointer(0) + YOffsets[MBNum];

        Y0 = lpVSegment->m_pDCTBlocks + MBNum*6;
        Y1 = Y0 + 1;
        Y2 = Y0 + 2;
        Y3 = Y0 + 3;

        for (LineNum = 0;LineNum < 8;LineNum++)
        {
            ReadYLine(Y0->m_lpsData, LineNum, lpcSrcY + 8*0);
            ReadYLine(Y1->m_lpsData, LineNum, lpcSrcY + 8*1);
            ReadYLine(Y2->m_lpsData, LineNum, lpcSrcY + 8*2);
            ReadYLine(Y3->m_lpsData, LineNum, lpcSrcY + 8*3);

            lpcSrcY += pSrcFrame->GetPlanePitch(0);
        }
    }

    lpcSrcY = (Ipp8u*)pSrcFrame->GetPlanePointer(0) + YOffsets[4];
    MBNum = 4;
    Y0 = lpVSegment->m_pDCTBlocks + MBNum*6;
    Y1 = Y0 + 1;
    Y2 = Y0 + 2;
    Y3 = Y0 + 3;

    for (LineNum = 0;LineNum < 8;LineNum++)
    {
        ReadYLine(Y0->m_lpsData, LineNum, lpcSrcY + 8*0);
        ReadYLine(Y1->m_lpsData, LineNum, lpcSrcY + 8*1);

        lpcSrcY += pSrcFrame->GetPlanePitch(0);
    }

    for (LineNum = 0;LineNum < 8;LineNum++)
    {
        ReadYLine(Y2->m_lpsData, LineNum, lpcSrcY + 8*0);
        ReadYLine(Y3->m_lpsData, LineNum, lpcSrcY + 8*1);

        lpcSrcY += pSrcFrame->GetPlanePitch(0);
    }

    for (MBNum = 0;MBNum < 4;MBNum++)
    {
        lpcSrcU = (Ipp8u*)pSrcFrame->GetPlanePointer(1) + UOffsets[MBNum];
        lpcSrcV = (Ipp8u*)pSrcFrame->GetPlanePointer(2) + VOffsets[MBNum];

        Cr = lpVSegment->m_pDCTBlocks + MBNum*6 + 4;
        Cb = Cr + 1;

        for (LineNum = 0;LineNum < 4;LineNum++)
        {
            ReadUVLine(Cr->m_lpsData, LineNum*2, lpcSrcV);
            ReadUVLine(Cb->m_lpsData, LineNum*2, lpcSrcU);

            lpcSrcU += pSrcFrame->GetPlanePitch(1);
            lpcSrcV += pSrcFrame->GetPlanePitch(2);
        }
    }

    lpcSrcU = (Ipp8u*)pSrcFrame->GetPlanePointer(1) + UOffsets[4];
    lpcSrcV = (Ipp8u*)pSrcFrame->GetPlanePointer(2) + VOffsets[4];

    MBNum = 4;
    Cr = lpVSegment->m_pDCTBlocks + MBNum*6 + 4;
    Cb = Cr + 1;
    for (LineNum = 0;LineNum < 4;LineNum++)
    {
        ReadHalfUVLine(Cb->m_lpsData,   LineNum*2, lpcSrcU);
        ReadHalfUVLine(Cb->m_lpsData+4, LineNum*2, lpcSrcU+pSrcFrame->GetPlanePitch(1));
        ReadHalfUVLine(Cr->m_lpsData,   LineNum*2, lpcSrcV);
        ReadHalfUVLine(Cr->m_lpsData+4, LineNum*2, lpcSrcV+pSrcFrame->GetPlanePitch(2));

        lpcSrcU += pSrcFrame->GetPlanePitch(1);
        lpcSrcV += pSrcFrame->GetPlanePitch(2);
    }
}

static
inline
void GetMBColAndRowInSBTypeA(Ipp16s MBOrder, Ipp16s &Col, Ipp16s &Row)
{
    Col = MBOrder/6;
    if(Col & 1)
        Row = 5 - (MBOrder % 6);
    else
        Row = (MBOrder % 6);
}

static
inline
void GetMBColAndRowInSBTypeB(Ipp16s MBOrder, Ipp16s &Col, Ipp16s &Row)
{
    Col = (MBOrder + 3)/6;
    if(Col & 1)
        Row = 5 - ( (MBOrder + 3) % 6 );
    else
        Row = ( (MBOrder + 3) % 6 );
}

static
inline
void GetMBColAndRowInSBTypeC(Ipp16s MBOrder, Ipp16s &Col, Ipp16s &Row)
{
    if(MBOrder >= 24)
    {
        Col = 4;
        Row = (MBOrder - 24)*2;
    }
    else
    {
        Col = MBOrder/6;

        if(Col & 1)
        {
            Row = 5 - (MBOrder  % 6);
        }
        else
        {
            Row = (MBOrder % 6);
        }
    }
}

const size_t DVSD_NTSC_MBLummaWidth = 32;
const size_t DVSD_NTSC_MBLummaHeight = 8;
const size_t DVSD_NTSC_MBChromaWidth420 = 16;
const size_t DVSD_NTSC_MBChromaHeight420 = 4;

const size_t DVSD_NTSC_SBLummaHeight = DVSD_NTSC_MBLummaHeight * 6;
const size_t DVSD_NTSC_SBChromaHeight420 = DVSD_NTSC_MBChromaHeight420 * 6;

static
void InitOffsets(size_t *YOffsets, size_t *UOffsets, size_t *VOffsets,
                 const size_t lPitches[], Ipp16s nNumOfDIFSeq, Ipp16s i, Ipp16s k)
{
    //i - DIF sequence order
    //k - order of the video segment in the DIF sequence

    Ipp16s ColA, RowA, ColB, RowB, ColC, RowC;
    GetMBColAndRowInSBTypeA(k, ColA, RowA);
    GetMBColAndRowInSBTypeB(k, ColB, RowB);
    GetMBColAndRowInSBTypeC(k, ColC, RowC);

    YOffsets[0] = (size_t) (
        // destination
        0 +
        // offset of needed SB line
        ((i + 2) % nNumOfDIFSeq) * lPitches[0] * DVSD_NTSC_SBLummaHeight +
        // offset of needed SB
        (4 + 5) * DVSD_NTSC_MBLummaWidth +
        // offset of MB in SB
        RowA * DVSD_NTSC_MBLummaHeight * lPitches[0] + ColA * DVSD_NTSC_MBLummaWidth);

    UOffsets[0] = (size_t) (
        // destination
        0 +
        // offset of needed SB line
        ((i + 2) % nNumOfDIFSeq) * lPitches[1] * DVSD_NTSC_SBChromaHeight420 +
        // offset of needed SB
        (4 + 5) * DVSD_NTSC_MBChromaWidth420  +
        // offset of MB in SB
        RowA * DVSD_NTSC_MBChromaHeight420 * lPitches[1] + ColA * DVSD_NTSC_MBChromaWidth420);

    VOffsets[0] = (size_t) (
        // destination
        0 +
        // offset of needed SB line
        ((i + 2) % nNumOfDIFSeq) * lPitches[2] * DVSD_NTSC_SBChromaHeight420 +
        // offset of needed SB
        (4 + 5) * DVSD_NTSC_MBChromaWidth420  +
        // offset of MB in SB
        RowA * DVSD_NTSC_MBChromaHeight420 * lPitches[2] + ColA * DVSD_NTSC_MBChromaWidth420);

    YOffsets[1] = (size_t) (0 +                                                       // destination
        ((i + 6) % nNumOfDIFSeq) * lPitches[0] * DVSD_NTSC_SBLummaHeight +              // offset of needed SB line
        (4) * DVSD_NTSC_MBLummaWidth +                                                  // offset of needed SB
        RowB * DVSD_NTSC_MBLummaHeight * lPitches[0] + ColB * DVSD_NTSC_MBLummaWidth);  // offset of MB in SB
    UOffsets[1] = (size_t) (0 +                                                       // destination
        ((i + 6) % nNumOfDIFSeq) * lPitches[1] * DVSD_NTSC_SBChromaHeight420 +          // offset of needed SB line
        (4) * DVSD_NTSC_MBChromaWidth420 +                                              // offset of needed SB
        RowB * DVSD_NTSC_MBChromaHeight420 * lPitches[1] + ColB * DVSD_NTSC_MBChromaWidth420);// offset of MB in SB
    VOffsets[1] = (size_t) (0 +                                                       // destination
        ((i + 6) % nNumOfDIFSeq) * lPitches[2] * DVSD_NTSC_SBChromaHeight420+           // offset of needed SB line
        (4) * DVSD_NTSC_MBChromaWidth420 +                                              // offset of needed SB
        RowB * DVSD_NTSC_MBChromaHeight420 * lPitches[2] + ColB * DVSD_NTSC_MBChromaWidth420);// offset of MB in SB

    YOffsets[2] = (size_t) (0 +                                                       // destination
        ((i + 8) % nNumOfDIFSeq) * lPitches[0] * DVSD_NTSC_SBLummaHeight +              // offset of needed SB line
        (4 + 5 + 4) * DVSD_NTSC_MBLummaWidth +                                          // offset of needed SB
        RowB * DVSD_NTSC_MBLummaHeight * lPitches[0] + ColB * DVSD_NTSC_MBLummaWidth);  // offset of MB in SB
    UOffsets[2] = (size_t) (0 +                                                       // destination
        ((i + 8) % nNumOfDIFSeq) * lPitches[1] * DVSD_NTSC_SBChromaHeight420 +          // offset of needed SB line
        (4 + 5 + 4) * DVSD_NTSC_MBChromaWidth420  +                                     // offset of needed SB
        RowB * DVSD_NTSC_MBChromaHeight420 * lPitches[1] + ColB * DVSD_NTSC_MBChromaWidth420);// offset of MB in SB
    VOffsets[2] = (size_t) (0 +                                                       // destination
        ((i + 8) % nNumOfDIFSeq) * lPitches[2] * DVSD_NTSC_SBChromaHeight420 +          // offset of needed SB line
        (4 + 5 + 4) * DVSD_NTSC_MBChromaWidth420  +                                     // offset of needed SB
        RowB * DVSD_NTSC_MBChromaHeight420 * lPitches[2] + ColB * DVSD_NTSC_MBChromaWidth420);// offset of MB in SB

    YOffsets[3] = (size_t) (0 +                                                       // destination
        ((i + 0) % nNumOfDIFSeq) * lPitches[0] * DVSD_NTSC_SBLummaHeight +              // offset of needed SB line
        (0) * DVSD_NTSC_MBLummaWidth +                                                  // offset of needed SB
        RowA * DVSD_NTSC_MBLummaHeight * lPitches[0] + ColA * DVSD_NTSC_MBLummaWidth);  // offset of MB in SB
    UOffsets[3] = (size_t) (0 +                                                       // destination
        ((i + 0) % nNumOfDIFSeq) * lPitches[1] * DVSD_NTSC_SBChromaHeight420 +          // offset of needed SB line
        (0) * DVSD_NTSC_MBChromaWidth420 +                                              // offset of needed SB
        RowA * DVSD_NTSC_MBChromaHeight420 * lPitches[1] + ColA * DVSD_NTSC_MBChromaWidth420);// offset of MB in SB
    VOffsets[3] = (size_t) (0 +                                                       // destination
        ((i + 0) % nNumOfDIFSeq) * lPitches[2] * DVSD_NTSC_SBChromaHeight420+           // offset of needed SB line
        (0) * DVSD_NTSC_MBChromaWidth420 +                                              // offset of needed SB
        RowA * DVSD_NTSC_MBChromaHeight420 * lPitches[2] + ColA * DVSD_NTSC_MBChromaWidth420);// offset of MB in SB

    YOffsets[4] = (size_t) (0 +                                                       // destination
        ((i + 4) % nNumOfDIFSeq) * lPitches[0] * DVSD_NTSC_SBLummaHeight +              // offset of needed SB line
        (4 + 5 + 4 + 5) * DVSD_NTSC_MBLummaWidth +                                      // offset of needed SB
        RowC * DVSD_NTSC_MBLummaHeight * lPitches[0] + ColC * DVSD_NTSC_MBLummaWidth);  // offset of MB in SB
    UOffsets[4] = (size_t) (0 +                                                       // destination
        ((i + 4) % nNumOfDIFSeq) * lPitches[1] * DVSD_NTSC_SBChromaHeight420 +          // offset of needed SB line
        (4 + 5 + 4 + 5) * DVSD_NTSC_MBChromaWidth420  +                                 // offset of needed SB
        RowC * DVSD_NTSC_MBChromaHeight420 * lPitches[1] + ColC * DVSD_NTSC_MBChromaWidth420);// offset of MB in SB
    VOffsets[4] = (size_t) (0 +                                                       // destination
        ((i + 4) % nNumOfDIFSeq) * lPitches[2] * DVSD_NTSC_SBChromaHeight420 +          // offset of needed SB line
        (4 + 5 + 4 + 5) * DVSD_NTSC_MBChromaWidth420  +                                 // offset of needed SB
        RowC * DVSD_NTSC_MBChromaHeight420 * lPitches[2] + ColC * DVSD_NTSC_MBChromaWidth420);// position MB in SB

}; //void InitOffsets(...)

void DVSD_NTSC_SegmentReader::ReadSegment(V_SEGMENT *lpVSegment, Ipp16s DIFSeqOrder, Ipp16s VSegmentOrder)
{
    size_t YOffsets[5], UOffsets[5], VOffsets[5];
    size_t Pitches[3];
    Pitches[0] = m_lpSourceFrame->GetPlanePitch(0);
    Pitches[1] = m_lpSourceFrame->GetPlanePitch(1);
    Pitches[2] = m_lpSourceFrame->GetPlanePitch(2);

    InitOffsets(YOffsets, UOffsets, VOffsets, Pitches, m_nNumOfDIFSeq, DIFSeqOrder, VSegmentOrder);

    if (VSegmentOrder >= 24)
    {
        ReadEndYUV(YOffsets, UOffsets, VOffsets, lpVSegment, m_lpSourceFrame);
    }
    else
    {
        ReadYUV(YOffsets, UOffsets, VOffsets, lpVSegment, m_lpSourceFrame);
    }
}

}//namespace UMC

#endif //(UMC_ENABLE_DV_VIDEO_ENCODER)
